<?php
declare(strict_types=1);
namespace app\admin\controller;
use aphp\core\Jump;

// CRUD基类
abstract class Base
{
    use Jump;
    protected string $middleware = 'rbac'; // rbac验证
    protected string $model; // 模型名称
    protected string $order = 'id DESC'; // 列表排序
    protected int $limit = 10; // 列表获取条数(0获取全部)
    protected string $fieldExcept = ''; // 列表排除字段,多个用,分开
    protected int $formSearch = 1; // 开启搜索表单
    protected int $urlSearch = 0; // 地址栏参数搜索
    protected string $jumpUrl = 'index'; // 成功跳转URL
    protected array $allowFill = ['*']; // 允许写入字段
    protected array $denyFill = []; // 禁止写入字段
    protected int $isRecycle = 0; // 开启回收站

    // 列表
    public function index()
    {
        if ($this->isAjax()) {
            $where = $this->_index_where();
            if ($this->isRecycle) {
                $where['delete_time'] = 0;
            }
            $this->_list_json($where);
        }
        return view();
    }

    // 查看
    public function detail(int $id)
    {
        $model = model($this->model)->find($id);
        if (!$model) {
            $this->error('记录不存在');
        }
        return view()->with('vo', $model->toArray());
    }

    // 新增
    public function add(array $req)
    {
        if ($this->isPost()) {
            $r = db()->trans(function() use ($req) {
                model($this->model)->save($req);
            });
            $this->_jump(['添加成功', '添加失败'], $r, $this->jumpUrl);
        }
        return view();
    }

    // 修改
    public function edit(int $id, array $req)    {
        $model = model($this->model)->find($id);
        if (!$model) {
            $this->error('记录不存在');
        }
        if ($this->isPost()) {
            $r = db()->trans(function() use ($model, $req) {
                $model->save($req);
            });
            $this->_jump(['修改成功', '修改失败'], $r, $this->jumpUrl);
        }
        return view()->with('vo', $model->toArray());
    }

    // 更新单字段
    public function multi(array $req)
    {
        if ($this->isPost()) {
            $ids = ids_filter($req['ids'], true);
            if (empty($ids)) {
                $this->error('请选择ID');
            }
            $field = $req['param'] ?? '';
            $value = $req['value'] ?? '';
            if ($field == '' || $value == '') {
                $this->error('参数不能为空');
            }
            $model = model($this->model);
            $data = $model->filterFieldFill([$field => $value], $this->allowFill, $this->denyFill); // 过滤
            if (empty($data)) {
                $this->error('字段禁止修改');
            }
            $errors = $model->validateField($field, $value);
            if (!empty($errors)) {
                $this->error(current($errors)); // 验证
            }
            $where = $this->_multi_where($field, $value);
            $where[] = [$field, '<>', $value];
            if (count($ids) == 1) {
                $where['id'] = current($ids);
            } else {
                $where[] = ['id', 'in', $ids];
            }
            $r = $model->where($where)->setField($field, $value);
            if ($r) $model->widgetReload();
            $this->_jump(['操作成功', '操作失败'], $r, $this->jumpUrl);
        }
        $this->error('非法操作');
    }

    // 删除
    public function del(string $ids)
    {
        if (!$this->isRecycle) {
            $this->destroy($ids);
            return;
        }
        $ids = ids_filter($ids, true);
        if (empty($ids)) {
            $this->error('请选择ID');
        }
        $map = [];
        $map['delete_time'] = 0;
        if (count($ids) == 1) {
            $map['id'] = current($ids);
        } else {
            $map[] = ['id', 'in', $ids];
        }
        $model = model($this->model);
        $deleteTime = time();
        $r = $model->where($map)->setField('delete_time', $deleteTime);
        $model->widgetReload();
        $this->_jump(['删除成功', '删除失败'], $r, $this->jumpUrl);
    }

    // 销毁
    public function destroy(string $ids)
    {
        $ids = ids_filter($ids, true);
        if (empty($ids)) {
            $this->error('请选择ID');
        }
        $map = [];
        if ($this->isRecycle) {
            $map[] = ['delete_time', '>', 0];
        } else {
            $map['status'] = 0;
        }
        $model = model($this->model);
        $count = 0; // 成功删除数量
        foreach ($ids as $id) {
            $tmp = $model->where($map)->find($id);
            if ($tmp) {
                $ok = db()->trans(function() use ($tmp) {
                    $tmp->del();
                });
                if ($ok) $count ++;
            }
        }
        $this->_jump(['删除成功', '删除失败，未停用或已禁删'], $count, $this->jumpUrl);
    }

    // 回收站
    public function recycle()
    {
        if (!$this->isRecycle) {
            $this->error('回收站未开启');
        }
        if ($this->isAjax()) {
            $where = $this->_recycle_where();
            $where[] = ['delete_time', '>', 0];
            $this->_list_json($where);
        }
        return view();
    }

    // 数据还原
    public function restore(string $ids)
    {
        $ids = ids_filter($ids, true);
        if (empty($ids)) {
            $this->error('请选择ID');
        }
        $map = [];
        $map[] = ['delete_time', '>', 0];;
        if (count($ids) == 1) {
            $map['id'] = current($ids);
        } else {
            $map[] = ['id', 'in', $ids];
        }
        $model = model($this->model);
        $r = $model->where($map)->setField('delete_time', 0);
        $model->widgetReload();
        $this->_jump(['还原成功', '还原失败'], $r, $this->jumpUrl);
    }

    // 列表JSON
    protected function _list_json(array $where = [])
    {
        $search = $this->_search();
        $where = array_merge($search, $where);
        $model = model($this->model);
        if ($this->limit > 0) {
            $limit = input('get.limit', $this->limit, 'intval');
            $page = input('get.page', 1, 'intval');
            $page = max(1,  $page); //获取当前在第几页
            $list = $model->field($this->fieldExcept, true)->where($where)->order($this->order)->page($page, $limit)->select();
             $count = $model->where($where)->count();
        } else {
            $list = $model->field($this->fieldExcept, true)->where($where)->order($this->order)->select();
            $count = count($list);
        }
        $list = $this->_list_parse($list);
        $this->_json(200, '', $list, ['count' => $count]);
    }

    // 更新字段预设条件
    protected function _multi_where(string $field, $value): array
    {
        return [];
    }

    // 首页列表预设条件
    protected function _index_where(): array
    {
        return [];
    }

    // 回收站列表预设条件
    protected function _recycle_where(): array
    {
        return $this->_index_where();
    }

    // 列表数据处理
    protected function _list_parse(array $list): array
    {
        return $list;
    }

    // 获取搜索条件
    protected function _search(): array
    {
        if ($this->urlSearch) {
            $where = input('get.where', '', 'clear_html');
            if (!empty($where)) {
                parse_str($where, $where);
                $where = array_diff($where, ['']); // 过滤空值
                if (empty($where)) {
                    return [];
                }
                $fields = model($this->model)->getFields();
                return array_filter($where, fn($k)=>in_array($k, $fields), ARRAY_FILTER_USE_KEY);
            }
        }
        if (!$this->formSearch) {
            return [];
        }
        $where = [];
        $search_field = input('get.search_field', '', 'clear_html'); // 字段@操作符
        $search_value = input('get.search_value', '', 'clear_html');
        if (!empty($search_field) && !empty($search_value)) {
            $op = '=';
            if (str_contains($search_field, '@')) {
                [$field, $op] = explode('@', $search_field, 2);
            } else {
                $field = $search_field;
            }
            $_where = $this->_where_parse($field, $op, $search_value);
            if (!empty($_where)) {
                $where[] = $_where;
            }
            return $where;
        }
        $filter = input('get.filter', '', 'clear_html');
        $op = input('get.op', '', 'clear_html');
        if (!empty($filter) && !empty($op)) {
            if (json_validate($filter) && json_validate($op)) {
                $filter = json_decode($filter, true);
                $op = json_decode($op, true);
                foreach ($filter as $k => $v) {
                    $_where = $this->_where_parse($k, $op[$k], $v);
                    if (!empty($_where)) {
                        $where[] = $_where;
                    }
                }
            }
        }
        return $where;
    }

    // 条件处理
    protected function _where_parse(string $field, string $op, $value): array
    {
        if ($value == '') {
            return [];
        }
        $op = strtoupper($op);
        if (in_array($op, ['IN', 'NOT IN', '>', '<','>=','<=', '<>'])) {
            return [$field, $op, ids_filter($value)];
        }
        if ($op == 'LIKE' || $op == 'NOT LIKE') {
            return [$field, $op, '%'.$value.'%'];
        }
        if ($op == 'RANGE' || $op == 'NOT RANGE') {
            [$start, $end] = explode(' - ', $value);
            $start = strtotime($start);
            $end = strtotime($end);
            $where_str = ($op == 'RANGE') ? $field.' >=? AND '.$field.' <=?' : $field.' <? AND '.$field.' >?';
            return [$where_str, [$start, $end]];
        }
        if ($op == 'BETWEEN' || $op == 'NOT BETWEEN') {
            [$min, $max] = explode(',', $value, 2);
            if (empty($min)) {
                $op = ($op == 'BETWEEN') ? '<=' : '>';
                $value = $max;
            } elseif (empty($max)) {
                $op = ($op == 'BETWEEN') ? '>=' : '<';
                $value = $min;
            } else {
                $value = [$min, $max];
            }
            return [$field, $op, $value];
        }
        if ($op == 'FIND_IN_SET') {

        }
        return [$field, $op, $value];
    }

}